#include "soc_gpio.h"
#include "cms8s6990.h"

#define UNPACK_PORT_PIN(portPin) (portPin >> 8) & 0xff, portPin & 0xff

void SOC_GpioInit(SOC_GpioPort_t port, int pin, SOC_GpioMode_t mode)
{
    uint8_t mask = (0x01 << pin);
    uint8_t tMode = GPIO_PUSH_PULL_MODE;

    switch (port)
    {
    case S_P0:
        P0OD &= ~mask;
        P0UP &= ~mask;
        P0RD &= ~mask;
        break;
    case S_P1:
        P1OD &= ~mask;
        P1UP &= ~mask;
        P1RD &= ~mask;
        break;
    case S_P2:
        P2OD &= ~mask;
        P2UP &= ~mask;
        P2RD &= ~mask;
        break;
    case S_P3:
        P3OD &= ~mask;
        P3UP &= ~mask;
        P3RD &= ~mask;
        break;
    
    default:
        break;
    }

    switch(mode)
    {
        case S_GPIO_Mode_IN_FLOATING:
        // GPIO_ENABLE_INPUT(P1TRIS, mask);
        break;
        case S_GPIO_Mode_IN_PULLDOWN:
        tMode = GPIO_DR_MODE;
        break;
        case S_GPIO_Mode_IN_PULLUP:
        tMode = GPIO_UP_MODE;
        break;
        case S_GPIO_Mode_Out_OD:
        tMode = GPIO_OPEN_DRAIN_MODE;
        break;
        case S_GPIO_Mode_Out_PP:
        tMode = GPIO_PUSH_PULL_MODE;
        switch (port)
        {
        case S_P0:
            GPIO_ENABLE_OUTPUT(P0TRIS, pin);
            break;
        case S_P1:
            GPIO_ENABLE_OUTPUT(P1TRIS, pin);
            break;
        case S_P2:
            GPIO_ENABLE_OUTPUT(P2TRIS, pin);
            break;
        case S_P3:
            GPIO_ENABLE_OUTPUT(P3TRIS, pin);
            break;
        
        default:
            break;
        }
        break;
    }

    GPIO_ConfigGPIOMode((uint8_t)port, mask, tMode);
}

void SOC_GpioWrite(SOC_GpioPort_t port, int pin, int val)
{
#define SET_PORT_BIT(p) \
    if(val == 0) \
        p &= ~(0x01 << pin); \
    else \
        p |= (0x01 << pin)

    switch(port)
    {
        case S_P0:
            SET_PORT_BIT(P0);
        break;
        case S_P1:
            SET_PORT_BIT(P1);
        break;
        case S_P2:
            SET_PORT_BIT(P2);
        break;
        case S_P3:
            SET_PORT_BIT(P3);
        break;
    }

#undef SET_PORT_BIT
}

void SOC_GpioTrigger(SOC_GpioPort_t port, int pin)
{
    switch(port)
    {
        case S_P0:
            P0 ^= (0x01 << pin);
        break;
        case S_P1:
            P1 ^= (0x01 << pin);
        break;
        case S_P2:
            P2 ^= (0x01 << pin);
        break;
        case S_P3:
            P3 ^= (0x01 << pin);
        break;
    }
}

int SOC_GpioRead(SOC_GpioPort_t port, int pin)
{
    switch(port)
    {
        case S_P0:
            return (P0 & (0x01 << pin)) != 0;
        break;
        case S_P1:
            return (P1 & (0x01 << pin)) != 0;
        break;
        case S_P2:
            return (P2 & (0x01 << pin)) != 0;
        break;
        case S_P3:
            return (P3 & (0x01 << pin)) != 0;
        break;
    }

    return 0;
}

void SOC_GpioSetItMode(SOC_GpioPort_t port, int pin, SOC_GpioItMode_t mode)
{
    uint8_t thisMode; 
    
    switch(mode)
    {
        case S_GPIO_IT_RISING:
            thisMode = GPIO_INT_RISING;
        break;
        case S_GPIO_IT_FALLING:
            thisMode = GPIO_INT_FALLING;
        break;
        case S_GPIO_IT_BOTH_EDGE:
            thisMode = GPIO_INT_BOTH_EDGE;
        break;
        default:
        return;
    }

#define P00EICFG_BASE_ADDR 0xF080
    switch(port)
    {
        case S_P0:
	        GPIO_SET_INT_MODE(*(volatile unsigned char xdata *)(P00EICFG_BASE_ADDR + pin), thisMode);	
        break;
        case S_P1:
	        GPIO_SET_INT_MODE(*(volatile unsigned char xdata *)(P00EICFG_BASE_ADDR + 8 + pin), thisMode);	
        break;
        case S_P2:
	        GPIO_SET_INT_MODE(*(volatile unsigned char xdata *)(P00EICFG_BASE_ADDR + 16 + pin), thisMode);	
        break;
        case S_P3:
	        GPIO_SET_INT_MODE(*(volatile unsigned char xdata *)(P00EICFG_BASE_ADDR + 24 + pin), thisMode);	
        break;
    }

	GPIO_EnableInt(port, 0x01 << pin);			
	// IRQ_SET_PRIORITY(IRQ_P2, IRQ_PRIORITY_LOW);
}

void SOC_GpioSet_IT(SOC_GpioPort_t port, int pin, bool en)
{
    if(en)
        GPIO_EnableInt(port, (0x01 << pin));
    else 
        GPIO_DisableInt(port, (0x01 << pin));
}

void SOC_GpioInitExt(uint16_t portPin, SOC_GpioMode_t mode)
{
    SOC_GpioInit(UNPACK_PORT_PIN(portPin), mode);
}

void SOC_GpioWriteExt(uint16_t portPin, int val)
{
    SOC_GpioWrite(UNPACK_PORT_PIN(portPin), val);
}

void SOC_GpioTriggerExt(uint16_t portPin)
{
    SOC_GpioTrigger(UNPACK_PORT_PIN(portPin));
}

int SOC_GpioReadExt(uint16_t portPin)
{
    return SOC_GpioRead(UNPACK_PORT_PIN(portPin));
}

void SOC_GpioSetItModeExt(uint16_t portPin, SOC_GpioItMode_t mode)
{
    SOC_GpioSetItMode(UNPACK_PORT_PIN(portPin), mode);
}

void SOC_GpioSet_ITExt(uint16_t portPin, bool en)
{
    SOC_GpioSet_IT(UNPACK_PORT_PIN(portPin), en);
}
